<!-- 嵌入式摄像头组件 -->

<template>
	<view style="overflow:hidden;">
		
		<!-- 摄像头外层容器 -->
		<focus-box 
		bg="#21252B"
		position="relative" 
		:width="config.width" 
		:height="config.height" 
		:styles="{'overflow':'hidden'}">
			
			<!-- 摄像头实例组件 -->
			<camera 
			mode="normal" 
			:flash="flash"
			frame-size="small"
			resolution="medium" 
			v-if="state.isDraw"
			:device-position="position" 
			:style="'width:'+config.width+'rpx; height:'+config.height+'rpx;'"
			@initdone="onInitdoneCall" 
			@error="onErrorCall"
			></camera>
			
			<!-- 组件外定位容器 -->
			<focus-box 
			top="0" 
			left="2500"
			position="absolute" 
			>
				<!-- 用于渲染处理实时帧数据的画布 -->
				<canvas 
				canvas-id="cavForCameraFrame" 
				style="background-color:#2C405A;"
				:style="'width:'+cavFrame.width+'px; height:'+cavFrame.height+'px;'"
				></canvas>
			</focus-box>
			
		</focus-box>
		
		<!-- 权限获取悬浮框 -->
		
		
	</view>
</template>

<script>
	// 通用方法引入
	import project from '@/common/js/public/project.js';
	
	/**
	 * 页面内嵌的区域相机组件
	 * @description 仅支持小程序，页面内嵌的区域相机组件，杜先生开发
	 * @property {String} tag 组件标识，默认：camera-embed
	 * @property {Array} size 显像摄像头容器宽高，单位：rpx。(推荐采用9:16的比例)
	 * @property {String} position = [front|back] 使用前置/后置摄像头，默认：back
	 *   @value front 前置摄像头
	 *   @value back 后置摄像头，默认
	 * @property {String} flash = [auto|on|off] 是否开启闪光灯，默认：off
	 *   @value auto 自动控制
	 *   @value on 打开闪光灯
	 *   @value off 关闭闪光灯，默认
	 * @property {String,Boolean} automatic = [false|true] 是否自动打开摄像头，默认：false
	 *   @value false 不会自动打开摄像头，需要手动调用open()，默认
	 *   @value true 会自动打开摄像头
	 * @event {Function()} initdone 相机初始化完成时触发
	 * @event {Function()} error 发生错误时触发
	 * @event {Function()} methods_open 父级refs方法open()：打开摄像头
	 * @event {Function()} methods_close 父级refs方法close()：关闭摄像头
	 * @event {Function()} methods_takePhoto 父级refs方法takePhoto()：进行拍照，返回照片临时地址
	 * @event {Function()} methods_onCameraFrame 父级refs方法onCameraFrame()：获取实时帧数据，返回base64格式
	 */
	export default {
		name: 'camera-embed',
		
		props: {
			tag: { type: String, default: 'camera-embed' },
			size: { type: Array, default: () => { return []; } },
			position: { type: String, default: 'back' },
			flash: { type: String, default: 'off' },
			automatic: { type: [String,Boolean], default: false },
		},
		
		// 页面数据
		data() {
			return {
				
				// 定时器对象
				timer: {
					draw: null, // 延时渲染摄像头dom元素 (定时器)
				},
				
				// 摄像头配置信息
				config: {
					width: 0, // 宽度rpx
					height: 0, // 高度rpx
					instance_frame: null, // 帧数据监听器对象
					timestamp: 0, // 上一次执行帧数据回调时候的时间戳
				},
				
				// 摄像头状态
				state: {
					isDraw: false, // 是否渲染摄像头dom对象
					isInit: false, // 是否初始化完毕
					isListening: false, // 是否正在监听帧数据
				},
				
				// 渲染处理实时帧数据的画布
				cavFrame: {
					width: 288, // 宽度 px
					height: 512, // 高度 px
				},
				
			}
		},
		
		// 数据监听
		watch: {
			// 摄像头成像框宽高 变化
			size: {
				handler(newValue, oldValue) {
					
					// 父级传入大小数据
					var width_camera = 0;
					var height_camera = 0;
					// 数组长度等于1时
					if (newValue.length == 1) { width_camera = newValue[0]; height_camera = newValue[0]; }
					// 否则用默认值
					else if (newValue.length > 1) { width_camera = newValue[0]; height_camera = newValue[1]; }
					// 其它情况下恢复默认值
					else { width_camera = 180; height_camera = 320; }
					
					// 当大小发生变化时候，执行
					if (width_camera != this.config.width || height_camera != this.config.height)
					{
						// 先关闭摄像头
						this.close(()=>{
							
							// 重设摄像头大小
							this.config.width = width_camera;
							this.config.height = height_camera;
							
							// 延时500ms 渲染摄像头dom元素
							if (this.timer.draw) { clearTimeout(this.timer.draw); }
							this.timer.draw = setTimeout(()=>{
								
								// 如果是自动打开摄像头
								if (this.automatic==true || this.automatic=='true')
								{
									// 打开摄像头
									this.open();
								}
								
							}, 500);
							
						});
					}
					
				},
				immediate: true,
				deep: true
			},
		},
		
		// 组件销毁
		beforeDestroy() {
			// 关闭摄像头
			this.close();
			// 销毁 定时器对象
			for (var key in this.timer) {  if (this.timer[key]) { clearTimeout(this.timer[key]); }  }
		},
		
		// 页面事件
		methods: {
			
			// =============================================== 父级$refs调用
			// 打开摄像头
			open() {
				// 渲染摄像头dom
				this.state.isDraw = true;
			},
			// 关闭摄像头
			close(callback) {
				// 摄像头初始化完成 才能执行
				if (this.state.isInit == true)
				{
					// 停止监听帧数据方法
					var fun_stopFrame = (endF) => {
						// 如果存在监听器对象
						if (this.state.isListening == true)
						{
							// 停止监听帧数据
							this.config.instance_frame.stop({
								fail: () => {  if (endF) { endF(); }  },
								success: () => {
									// 初始化监听器对象
									this.config.instance_frame = null;
									// 修改状态数值
									this.state.isListening = false;
									// 执行回调
									if (endF) { endF(); }
								},
							});
						}
						// 直接执行回调
						else { if (endF) { endF(); } }
					};
					
					// 执行方法：停止监听帧数据
					fun_stopFrame(()=>{
						// 销毁摄像头dom
						this.state.isDraw = false;
						// 修改状态数值
						this.state.isInit = false;
						// 关闭完成，执行回调
						if (callback) { callback(); }
					});
				}
				// 说明本来就是关闭的
				else {  if (callback) { callback(); }  }
			},
			
			// 拍照
			takePhoto(callback) {
				// 摄像头初始化完成 才能执行
				if (this.state.isInit == true)
				{
					// 获取摄像头实例对象
					var instance_camera = uni.createCameraContext();
					// 进行拍照
					instance_camera.takePhoto({
						quality: 'high',
						fail: (res) => {
							// 错误提示
							uni.showToast({
								title: '拍照失败，请检查设备',
								icon: 'none',
								duration: 2500,
							});
						},
						success: (res) => {
							// 返回数据封装
							var back_takePhoto = {
								path: res.tempImagePath,
							};
							// 执行回调函数
							if (callback) { callback(back_takePhoto); }
						},
					});
				}
			},
			// 获取实时帧数据
			onCameraFrame(callback) {
				// 摄像头初始化完成 才能执行
				if (this.state.isInit == true)
				{
					// 获取摄像头实例对象
					var instance_camera = uni.createCameraContext();
					// 帧数据监听器设置
					this.config.instance_frame = instance_camera.onCameraFrame((frame) => {
						// 帧数据监听内存优化处理
						this.dealCameraFrameData(frame, (obj_call)=>{
							// 执行回调函数
							if (callback) { callback(obj_call); }
						});
					});
					// 帧数据获取开始
					this.config.instance_frame.start({
						success: () => {
							// 修改状态数值
							this.state.isListening = true;
						}
					});
				}
			},
			
			
			// =============================================== 数据处理
			// 相机初始化完成触发
			onInitdoneCall() {
				// 修改状态数值
				this.state.isInit = true;
				// 进行"@"回调方法： 摄像头dom元素渲染完毕时触发
				this.$emit('initdone', { "tag": this.tag });
			},
			
			// 摄像头报错触发
			onErrorCall(ev) {
				// 进行"@"回调方法： 摄像头dom元素渲染完毕时触发
				this.$emit('error', { "tag": this.tag, "info": ev });
			},
			
			// 帧数据监听内存优化处理，参数：frame-相机返回的帧数据
			dealCameraFrameData(frame, callback) {
				// 获取当前时间戳
				var time_now = new Date().getTime();
				// 时间戳判定
				if (time_now - this.config.timestamp > 1000)
				{
					// 时间戳覆盖
					this.config.timestamp = time_now;
					// 画布大小赋值
					this.cavFrame.width = frame.width;
					this.cavFrame.height = frame.height;
					
					// 将 ArrayBuffer 转换为 Uint8ClampedArray
					var data = new Uint8Array(frame.data);
					var clamped = new Uint8ClampedArray(data);
					// 画布渲染相机返回的数据流
					wx.canvasPutImageData({
						canvasId: 'cavForCameraFrame',
						data: clamped,
						x: 0,
						y: 0,
						width: frame.width,
						height: frame.height,
						success: () => {
							// 从画布再导出图片
							wx.canvasToTempFilePath({
								canvasId: 'cavForCameraFrame',
								x: 0,
								y: 0,
								width: this.cavFrame.width,
								height: this.cavFrame.height,
								destWidth: this.cavFrame.width,
								destHeight: this.cavFrame.height,
								fileType: 'jpg',
								quality: 0.9,
								success: (cavImg) => {
									
									// 将图片转换为base64
									project.imageToBase64({
										src: cavImg.tempFilePath, // 图片src属性值
										errToast: false, // 是否提示错误
										success: (obj_baseD) => {
											// 数据回调
											if (callback) { callback({faceSrc: cavImg.tempFilePath, ...obj_baseD}); }
										},
									});
									
								}
							}, this);
						}
					}, this);
					
				}
			},
			
		}
	
	}
</script>